iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 5
0
Modern Web

關於React,那些我不知道的系列 第 5

所以昨天說的那個 useCallback 是幹啥用的? 效能優化? 來瞧瞧

  • 分享至 

  • xImage
  •  

demo codesandbox
Why

當 props 是一個callback function 時,React.memo 需要搭配 useCallback 使用

我們先來看看 props 是個 callback function 時,我們不做些什麼會怎樣

  1. App 當中有個 statebar ,還有個按鈕,每次點它都會更新 bar ,使 App 被重新渲染。
  2. App 當中有個 callback funtion handleClick,作為 props ,傳遞下去給 <Foo /> 元件。
  3. 我們使用 React.memo<Foo /> 元件做優化,希望 handleClick 作為 props 傳遞下來,期待此props 沒有變化時,我們就不重新渲染 <Foo>
  4. 點擊 App 中的【點我】按鈕嘗試更新 bar
  5. 期待我們的 <Foo /> 不被重新渲染 ( 因為handleClick 這個 callback 至始至終都一樣 )
function App() {
  const [bar, setBar] = useState(0);
  const handleClick = () => console.log("Button is clicked");
  return (
    <div className="App">
      <button onClick={() => setBar((bar) => bar + 1)}>點我</button>
      Render App {bar}
      <Foo handleClick={handleClick} />
    </div>
  );
}
const Foo = React.memo(({ handleClick }) => {
  console.log(" === render Foo ===");
  return (
    <div className="foo">
      <div>
        <button onClick={handleClick}>點我</button>
        執行 Callback from App (props)
      </div>
    </div>
  );
});

結果打開我們的 console ,發現 點擊 App 中的【點我】按鈕嘗試更新 bar,底下 <Foo /> 元件都會被重新渲染。因為...

  1. bar 每次更新時,App 都會重新渲染,因此也會重新執行 const handleClick = () => console.log("Button is clicked"); 這一行。
  2. 對於 <Foo /> 元件,看似相同的 handleClick 作為 props 傳遞下來,實際為不相同的東西,此話怎講?
    回到 JavaScript 語言本身
8787 === 8787 // true 數字型別
"帥" === "帥" // true 字串型別
true === true // true 布林型別

[] === [] // false 陣列型別
{} === {} // false 物件型別
const a = () => {}, b = () => {}
a === b   // false  function 是物件的子型別
  1. 在 App 兩次渲染當中,previous props 跟 next props , 提供的這個 handleClick 對於 <Foo /> 元件而言,是不相同的東西。 可參考JavaScript 是「傳值」或「傳址」?
  2. 因此當這個 callback function,也很昂貴的話,我們希望透過 useCallback 記憶住它。

所以說當 props 是 callback 時, useCallbackReact.memo 是個組合技,兩者必須一起使用。

題外話補充
呼應昨天~也因此你的 props 是 物件(Object) 或 陣列(Array)時, React.memo 才會另外提供第二個參數,協助我們做更精確的判斷

function areEqual(prevProps, nextProps) {
 /*
   只 return true 或 false
 */
}`

How
show me the code
demo codesandbox
在原本的code當中

- const handleClick = () => console.log("Button is clicked");  
+ const handleClick = useCallback(() => console.log("Button is clicked"), []);

React 官網也有提到 useCallback(fn, deps) 相等於 useMemo(() => fn, deps)

而關於 useCallback 第二個參數 deps,則是你希望當什麼 state / props 發生改變時,你希望重新創建這個 function 的 instance。

而關於 useCallback 還有其他很棒的參考文章,提供連結如下:
如何錯誤地使用 React hooks useCallback 來保存相同的 function instance
大大的範例真的幫助很多QwQ 感恩!
彻底理解 React hook useCallback和useMemo的区别
Your Guide to React.useCallback()
【译】什么时候使用 useMemo 和 useCallback
什麼時候該使用 useMemo 跟 useCallback
React Hooks(二): useCallback 之痛
React 性能優化那件大事,使用 memo、useCallback、useMemo

總結

1.什麼時候該使用 useCallback ?

  1. 昂貴的計算
  2. 你的 props 是引用 reference ,像是 callback function

2.效能優化是有代價的,請審慎評估

3.注意要正確放入 dependencies Array,避免產生非預期的效果

4.當 props 是 callback 時, useCallbackReact.memo 是個組合技,兩者必須一起使用。

參考資料

深入探討 JavaScript 中的參數傳遞:call by value 還是 reference?
重新認識 JavaScript: Day 05 JavaScript 是「傳值」或「傳址」?
如何錯誤地使用 React hooks useCallback 來保存相同的 function instance
【译】什么时候使用 useMemo 和 useCallback


上一篇
關於props的記憶,React Memo (新增範例及說明)
下一篇
實現跨元件資料共享, useContext
系列文
關於React,那些我不知道的30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言